package doc2db.fileWorker.xml;

import doc2db.fileWorker.*;
import doc2db.Keey;
import doc2db.UserProp;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JOptionPane;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;


/**
 *  <p>Класс разбора XML докумпнов</p>
 * @author yura
 */
public class XMLParser {
    /**название тегов заголовков*/
    private String head [];
    /**название тегов инфо. части*/
    private String info [];
    /**название тегов инфо. части таблици*/
    private String tab_info [];
    /**название тегов БД части таблици*/
    private String db_info [];
    
    /**неучитывать данные заголовки*/
    private boolean nullInHead = false;
    /**неучитывать данные заголовки*/
    private boolean nullInInfo = false;
    /**неучитывать данные заголовки*/
    private boolean nullInTabinfo = false;
    /**неучитывать данные заголовки*/
    private boolean nullInDbInfo = false;

    /**является ли данная структура, xml документа, древовидной*/
    private boolean isTreeType = false;

    /**информация между каждым тегом заголовка*/
    private String headInfo[] = null;
    /**информация между каждым тегом тела*/
    private String infoInfo[] = null;

    /**древовидное содержание xml документа*/
    private TreeTypeNodes tree = null;

    /**параметры таблици*/
    private TableParam tableParam = new TableParam();
    /**параметры Db*/
    private DbParam dbParam = new DbParam ();

    /**
     *   <p>Метод инициализации XML докумена содержащий
     * название тегов документа, разберающий документ(.doc)
     * <b>Инициализация этого метода необходима, иначе обработка
     * не произойдёт<b></p>
     * @throws javax.xml.parsers.ParserConfigurationException
     * @throws org.xml.sax.SAXException
     * @throws java.io.IOException
     */
    public void initTagsName() throws ParserConfigurationException, SAXException, IOException{
        NodeList children = this.beginer(FileBeginer.resurse_users_usersFolder + Keey.sp
                + UserProp.user + Keey.sp + FileBeginer.metaTemplatesName);//root.getChildNodes();

        if (children == null) {
            return;
        }

        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);
            if (child instanceof Element) {
                Element childElem = (Element)child;
                Text textNode = (Text) childElem.getFirstChild();
                String text = textNode.getData().trim();
                //System.out.println(i+")"+childElem.getTagName()+"="+text);
                if (childElem.getTagName().equals("head")) {
                    if (text.equals("null")) {
                        head = null;
                        nullInHead = true;
                    } else {
                        head = this.getCleanArray(text.split("\\s+"));
                    }
                }else if(childElem.getTagName().equals("info")) {
                    if (text.equals("null")) {
                        info = null;
                        nullInInfo = true;
                    } else {
                        info = this.getCleanArray(text.split("\\s+"));
                    }
                }else if(childElem.getTagName().equals("tab_info")) {
                    if (text.equals("null")) {
                        tab_info = null;
                        nullInTabinfo = true;
                    } else {
                        tab_info = this.getCleanArray(text.split("\\s+"));
                    }
                }else if(childElem.getTagName().equals("db_info")) {
                    if (text.equals("null")) {
                        db_info = null;
                        nullInDbInfo = true;
                    } else {
                        db_info = this.getCleanArray(text.split("\\s+"));
                    }
                }
            }
        }
    }

    /**
     * <p>Метод вычитывает данные из тегов XML документа описывающий
     * обрабатываемый документ.</p>
     * <p>Перед вызоом данного метода необходимо вызвать метод
     * <b>initTagsName<b> , или возникнет ошибка</p>
     * @throws javax.xml.parsers.ParserConfigurationException
     * @throws org.xml.sax.SAXException
     * @throws java.io.IOException
     */
    public void insertInfo () throws ParserConfigurationException, SAXException, IOException {
        List<String> localHeadInfo = new ArrayList<String>();
        List<String> localInfoInfo = new ArrayList<String>();

        String tegName = ""; //head or info
        NodeList mainList;   //<head> <info> <tab_info> <db_info>
        Node mainNode;       //mainList.item(i);
        NodeList childList;  //mainNode.getChildNodes(); <head0> <head1> <tab_info>
        Node childNode;      //childList.item(i);
        mainList = beginer(FileBeginer.resurse_users_usersFolder + Keey.sp
                + UserProp.user + Keey.sp + FileBeginer.templatesName);

        if (mainList == null) {
            nullInHead = true;
            nullInInfo = true;
            nullInTabinfo = true;
            nullInDbInfo = true;
            return;
        }

        for (int i = 0; i < mainList.getLength(); i++) {
            mainNode = mainList.item(i);
            if(mainNode instanceof Element){
                //continue;
            }else{continue;}

            Element mainElem = (Element) mainNode;
            if (mainElem.getTagName().toLowerCase().equals("head") ) {
                tegName = "head";
            } else if (mainElem.getTagName().toLowerCase().equals("info")) {
                tegName = "info";
                isTreeType = Boolean.parseBoolean("" + mainElem.getAttribute("tree_type").toLowerCase());
                if (isTreeType) {
                    TreeTypeTreatment treatment = new TreeTypeTreatment(info, mainList.item(i));
                    if (treatment.parseNode()) {
                        tree = treatment.getTreeTypeNodes();
                        if(Keey.debug){
                            System.out.println("Tree of xml");
                            treatment.print();
                        }
                    } else {
                        isTreeType = false;
                        System.err.println("Can read tree");
                    }
                }
            } else if (mainElem.getTagName().toLowerCase().equals("tab_info")) {
                tegName = "tab_info";
            }else if (mainElem.getTagName().toLowerCase().equals("db_info")) {
                tegName = "db_info";
            }else {
                tegName = mainElem.getTagName();
                System.err.println("  >Unexpected name of heading tag \"" +
                            tegName+"\"");
                continue;
            }

            childList = mainNode.getChildNodes();

            for (int j = 0; j < childList.getLength(); j++) {
                childNode = childList.item(j);
                if(childNode instanceof Element){ }else{
                    continue;
                }

                Element childElem = (Element) childNode;
                Text textNode = (Text) childElem.getFirstChild();
                String text = textNode.getData().trim();
/**///                System.out.println(j+")<"+childElem.getTagName()+">"+text+"</"+childElem.getTagName()+">");
                if (tegName.equals("head")) {
                    if(nullInHead)
                        continue;
                    if (this.isInMetaTemp(0, childElem.getTagName())) {
                        if (text.equals("null")) {
                            localHeadInfo.add(text);
                        } else {
                            localHeadInfo.add(this.getCleanStr(text));
                        }
                    }else
                        System.err.println("  >tag \""+childElem.getTagName()+
                                "\" from \""+tegName+"\"not declared");
                }else if(tegName.equals("info")) {
                    if(nullInInfo)
                        continue;
                    if(isTreeType){
                        localInfoInfo.add(text);
                    }else{
                        if (this.isInMetaTemp(1, childElem.getTagName())) {
                            if (text.equals("null")) {
                                localInfoInfo.add(text);
                            } else {
                                localInfoInfo.add(this.getCleanStr(text));
                            }
                        }else {
                            System.err.println("  >tag \""+childElem.getTagName()+
                                    "\" from \""+tegName+"\" not declared");
                        }
                    }
                }else if(tegName.equals("tab_info")) {
                    if(nullInTabinfo)
                        continue;
                    if (this.isInMetaTemp(2, childElem.getTagName())) {
                        if(childElem.getTagName().equals("uniteTable")){
                            tableParam.setUniteTable(this.getCleanStr(text));
                        }else if(childElem.getTagName().equals("changeHeadName")){
                            tableParam.setChangeHeadName(this.getCleanStr(text));
                        }else if(childElem.getTagName().equals("myHeadName")){
                            tableParam.setMyHeadName(this.getCleanStr(text));
                        }else if(childElem.getTagName().equals("treeTable")){
                            tableParam.setTreeTable(this.getCleanStr(text),
                                    childElem.getAttribute("len"));
                        }
                    } else {
                        System.err.println("  >tag \"" + childElem.getTagName() +
                                "\" from \"" + tegName + "\" not declared");
                    }
                }else if(tegName.equals("db_info")){
                    if(nullInDbInfo)
                        continue;
                    if (this.isInMetaTemp(3, childElem.getTagName())) {
                        if(childElem.getTagName().equals("encoding")){
                            dbParam.setClientEncoding(this.getCleanStr(text));
                        }else if(childElem.getTagName().equals("headType")){
                            dbParam.setHeadType(this.getCleanStr(text));
                        }
                    }else {
                        System.err.println("  >tag \"" + childElem.getTagName() +
                                "\" from \"" + tegName + "\" not declared");
                    }
                }else{
                    System.err.println("  >Unexpected name of heading tag \"" +
                            tegName+"\"");
                }
            }
        }

/** /    System.err.println("isNullInHead="+nullInHead);
        if(!nullInHead) nullInHead = ! isTreeType;
        if(!nullInInfo) nullInInfo = ! isTreeType;
/** /    System.err.println("isNullInHead="+nullInHead);*/

        headInfo = new String[localHeadInfo.size()];
        for (int i = 0; i < headInfo.length; i++) {
            headInfo[i] = localHeadInfo.get(i);
        }
        infoInfo = new String[localInfoInfo.size()];
        for (int i = 0; i < infoInfo.length; i++) {
            infoInfo[i] = localInfoInfo.get(i);
        }
    }

    /**
     * <p>Тест принадлижит ли данный тег описанию </p>
     * @param where 0 - head, 1 - info, 2 - tab_info, 3 - db_info
     * @param what имя тега
     * @return да/нет
     */
    private boolean isInMetaTemp(int where, String what){
        if(where == 0){
            for (int i = 0; i < head.length; i++) {
                if(head[i].equals(what))
                    return true;
            }
            return false;
        }else if(where == 1){
            for (int i = 0; i < info.length; i++) {
                if(info[i].equals(what))
                    return true;
            }
            return false;
        }else if(where == 2){
            for (int i = 0; i < tab_info.length; i++) {
                if(tab_info[i].equals(what))
                    return true;
            }
            return false;
        }else if(where == 3){
            for (int i = 0; i < db_info.length; i++) {
                if(db_info[i].equals(what))
                    return true;
            }
            return false;
        }else{
            return false;
        }
    }

    /**
     * <p>инициализация документа</p>
     * @param fileName имя XML документа
     * @return <p>главный тег документа</p>
     * @throws org.xml.sax.SAXException
     * @throws java.io.IOException
     * @throws javax.xml.parsers.ParserConfigurationException
     */
    private NodeList beginer(String fileName) throws SAXException,
            IOException, ParserConfigurationException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        factory.setNamespaceAware(true);
        factory.setValidating(true);
        factory.setExpandEntityReferences(false);

        File f = new File(fileName); //NEEDED TO CHANGE(user)
        Document doc = null;
        try {
            doc = builder.parse(f);
        } catch (Exception e) {
            System.err.println( "\tXML data will not be take into account\n\t"
                    + e + "\n\tIn file : " + f.getName());
            String msg = "error in template (" + f.getName() + ")\n" + e.getLocalizedMessage();
            JOptionPane.showMessageDialog(null,msg, "doc2db", JOptionPane.ERROR_MESSAGE);
            return null;
        }
        Element root = doc.getDocumentElement();
        NodeList children = root.getChildNodes();
        return children;
    }

    /**
     *   <p>Очищяет масив строк, каждаяя строка которого
     * заключена в кавычки, от боковых кавычек</p>
     * @param durtyArr  <p>очищяемый масив</p>
     * @return  <p>очищенный масив</p>
     */
    private String [] getCleanArray(String durtyArr []){
        char date [] [] = new char [durtyArr.length][];
        String[] retStr = new String [date.length];

        for (int i = 0; i < date.length; i++) {
            date[i] = new char[durtyArr[i].length()-2];
            for (int j = 0; j < date[i].length; j++) {
               date[i][j] = durtyArr[i].charAt(j+1);
            }
            retStr[i] = new String(date[i]);
        }

        return retStr;
    }

    /**
     * <p>Очищяет строкy, удаляя боковые символы:
     * первый и последний</p>
     * @param str <p>очищяемая строка</p>
     * @return <p>очищенная строка</p>
     */
    private String getCleanStr(String str){
        char data [] = new char[str.length()-2];
        for (int i = 0; i < data.length; i++) {
            data[i] = str.charAt(i + 1);
        }
        return new String (data) ;
    }

    /**
     * 
     * @return <p>название тегов заголовков</p>
     */
    public String[] getHead() {
        return head;
    }

    /**
     * 
     * @return <p>информация между каждым тегом заголовка</p>
     */
    public String[] getHeadInfo() {
        return headInfo;
    }

    /**
     * 
     * @return <p>название тегов инфо. части</p>
     */
    public String[] getInfo() {
        return info;
    }

    /**
     * 
     * @return <p>информация между каждым тегом тела</p>
     */
    public String[] getInfoInfo() {
        return infoInfo;
    }

    /**
     *
     * @return <p>название тегов описания таблиц</p>
     */
    public String[] getTab_info() {
        return tab_info;
    }

    /**
     *  <p>есть ли теги верхнего уровня</p>
     * @return да/нет
     */
    public boolean isNullInHead() {
        return nullInHead;
    }

    /**
     * <p>есть ли теги уровня таблици</p>
     * @return да/нет
     */
    public boolean isNullInInfo() {
        return nullInInfo;
    }

    /**
     * <p>есть ли теги описания таблици</p>
     * @return да/нет
     */
    public boolean isNullInTabinfo() {
        return nullInTabinfo;
    }

    /**
     * является ли данная структура, xml документа, древовидной
     * @return true, если является и false если - нет
     */
    public boolean isIsTreeType() {
        return isTreeType;
    }


    /**
     * Древовидная структура описания документа
     * @return описание документа
     */
    public TreeTypeNodes getTreeTypeNodes(){
        return tree;
    }


    /**
     * <p>параметры таблиц</p>
     * @return параметры таблиц
     */
    public TableParam getTableParam(){
        return tableParam;
    }

    public DbParam getDbParam(){
        return dbParam;
    }
    
    
}
